package rollercoaster.train;

import rollercoaster.commons.Vertex3D;
import rollercoaster.commons.interfaces.Surface3D;
import rollercoaster.railway.utils.VertexUtils;

public class UniformOpenSecondOrderPatch implements Surface3D{
	private Vertex3D[][] controlPoints;
	
	private float new_u;
	private float new_v;
	
	private int interval_u;
	private int interval_v;
	
	private Vertex3D[][] subMatrix = new Vertex3D[3][3];

	public UniformOpenSecondOrderPatch(Vertex3D[][] controlPoints) {
		super();
		this.controlPoints = controlPoints;
	}

	public Vertex3D[][] getControlPoints() {
		return controlPoints;
	}

	public void setControlPoints(Vertex3D[][] controlPoints) {
		this.controlPoints = controlPoints;
	}

	public Vertex3D sigma(float u, float v) {
		focusOnSubmatrix(u,v);
		
		if(interval_u==0){
			if(interval_v==0){
				float gamma1x=controlPoints[0][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getX()-controlPoints[0][0].getX());
				float gamma1y=controlPoints[0][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getY()-controlPoints[0][0].getY());
				float gamma1z=controlPoints[0][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getZ()-controlPoints[0][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=controlPoints[1][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getX()-controlPoints[1][0].getX());
				float gamma2y=controlPoints[1][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getY()-controlPoints[1][0].getY());
				float gamma2z=controlPoints[1][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getZ()-controlPoints[1][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=gamma1.getX()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
				float sigmay=gamma1.getY()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
				float sigmaz=gamma1.getZ()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else if(interval_v==controlPoints.length-1){
				float gamma1x=controlPoints[controlPoints.length-2][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getX()-controlPoints[controlPoints.length-2][0].getX());
				float gamma1y=controlPoints[controlPoints.length-2][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getY()-controlPoints[controlPoints.length-2][0].getY());
				float gamma1z=controlPoints[controlPoints.length-2][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getZ()-controlPoints[controlPoints.length-2][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=controlPoints[controlPoints.length-1][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getX()-controlPoints[controlPoints.length-1][0].getX());
				float gamma2y=controlPoints[controlPoints.length-1][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getY()-controlPoints[controlPoints.length-1][0].getY());
				float gamma2z=controlPoints[controlPoints.length-1][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getZ()-controlPoints[controlPoints.length-1][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=VertexUtils.meanPoint(gamma1, gamma2).getX()+new_v*(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
				float sigmay=VertexUtils.meanPoint(gamma1, gamma2).getY()+new_v*(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
				float sigmaz=VertexUtils.meanPoint(gamma1, gamma2).getZ()+new_v*(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else {
				float gamma1x=controlPoints[interval_v-1][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getX()-controlPoints[interval_v-1][0].getX());
				float gamma1y=controlPoints[interval_v-1][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getY()-controlPoints[interval_v-1][0].getY());
				float gamma1z=controlPoints[interval_v-1][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getZ()-controlPoints[interval_v-1][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=controlPoints[interval_v][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getX()-controlPoints[interval_v][0].getX());
				float gamma2y=controlPoints[interval_v][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getY()-controlPoints[interval_v][0].getY());
				float gamma2z=controlPoints[interval_v][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getZ()-controlPoints[interval_v][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float gamma3x=controlPoints[interval_v+1][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getX()-controlPoints[interval_v+1][0].getX());
				float gamma3y=controlPoints[interval_v+1][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getY()-controlPoints[interval_v+1][0].getY());
				float gamma3z=controlPoints[interval_v+1][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getZ()-controlPoints[interval_v+1][0].getZ());
				
				Vertex3D gamma3=new Vertex3D(gamma3x, gamma3y, gamma3z);
				
				Vertex3D[] gammas={gamma1,gamma2,gamma3};
				
				Vertex3D start=VertexUtils.meanPoint(gammas[0], gammas[1]);
				Vertex3D end=VertexUtils.meanPoint(gammas[1], gammas[2]);
				
				float sigma_x=(1-new_v)*(1-new_v)*start.getX() + 2*new_v*(1-new_v)*gammas[1].getX() + new_v*new_v*end.getX();
				float sigma_y=(1-new_v)*(1-new_v)*start.getY() + 2*new_v*(1-new_v)*gammas[1].getY() + new_v*new_v*end.getY();
				float sigma_z=(1-new_v)*(1-new_v)*start.getZ() + 2*new_v*(1-new_v)*gammas[1].getZ() + new_v*new_v*end.getZ();
				
				Vertex3D sigma=new Vertex3D(sigma_x, sigma_y, sigma_z);
				
				return sigma;
			}
		}
		
		if(interval_u==controlPoints[0].length-1){
			if(interval_v==0){
				float gamma1x=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getX()+new_u*(controlPoints[0][interval_u].getX()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getX());
				float gamma1y=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getY()+new_u*(controlPoints[0][interval_u].getY()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getY());
				float gamma1z=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getZ()+new_u*(controlPoints[0][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getX()+new_u*(controlPoints[1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getX());
				float gamma2y=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getY()+new_u*(controlPoints[1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getY());
				float gamma2z=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getZ()+new_u*(controlPoints[1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=gamma1.getX()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
				float sigmay=gamma1.getY()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
				float sigmaz=gamma1.getZ()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else if (interval_v==controlPoints.length-1){
				float gamma1x=VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getX()+new_u*(controlPoints[controlPoints.length-2][interval_u].getX()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getX());
				float gamma1y=VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getY()+new_u*(controlPoints[controlPoints.length-2][interval_u].getY()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getY());
				float gamma1z=VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getZ()+new_u*(controlPoints[controlPoints.length-2][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getX()+new_u*(controlPoints[controlPoints.length-1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getX());
				float gamma2y=VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getY()+new_u*(controlPoints[controlPoints.length-1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getY());
				float gamma2z=VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getZ()+new_u*(controlPoints[controlPoints.length-1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=VertexUtils.meanPoint(gamma1, gamma2).getX()+new_v*(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
				float sigmay=VertexUtils.meanPoint(gamma1, gamma2).getY()+new_v*(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
				float sigmaz=VertexUtils.meanPoint(gamma1, gamma2).getZ()+new_v*(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else {
				float gamma1x=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getX()+new_u*(controlPoints[interval_v-1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getX());
				float gamma1y=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getY()+new_u*(controlPoints[interval_v-1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getY());
				float gamma1z=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getZ()+new_u*(controlPoints[interval_v-1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getX()+new_u*(controlPoints[interval_v][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getX());
				float gamma2y=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getY()+new_u*(controlPoints[interval_v][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getY());
				float gamma2z=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getZ()+new_u*(controlPoints[interval_v][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float gamma3x=VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getX()+new_u*(controlPoints[interval_v+1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getX());
				float gamma3y=VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getY()+new_u*(controlPoints[interval_v+1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getY());
				float gamma3z=VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getZ()+new_u*(controlPoints[interval_v+1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getZ());
				
				Vertex3D gamma3=new Vertex3D(gamma3x, gamma3y, gamma3z);
				
				Vertex3D[] gammas={gamma1,gamma2,gamma3};
				
				Vertex3D start=VertexUtils.meanPoint(gammas[0], gammas[1]);
				Vertex3D end=VertexUtils.meanPoint(gammas[1], gammas[2]);
				
				float sigma_x=(1-new_v)*(1-new_v)*start.getX() + 2*new_v*(1-new_v)*gammas[1].getX() + new_v*new_v*end.getX();
				float sigma_y=(1-new_v)*(1-new_v)*start.getY() + 2*new_v*(1-new_v)*gammas[1].getY() + new_v*new_v*end.getY();
				float sigma_z=(1-new_v)*(1-new_v)*start.getZ() + 2*new_v*(1-new_v)*gammas[1].getZ() + new_v*new_v*end.getZ();
				
				Vertex3D sigma=new Vertex3D(sigma_x, sigma_y, sigma_z);
				
				return sigma;
			}
		}
		
		if(interval_v==0){
			
			Vertex3D start=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]);
			Vertex3D end=VertexUtils.meanPoint(controlPoints[0][interval_u], controlPoints[0][interval_u+1]);
			
			float gamma1x=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*controlPoints[0][interval_u].getX() + new_u*new_u*end.getX();
			float gamma1y=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*controlPoints[0][interval_u].getY() + new_u*new_u*end.getY();
			float gamma1z=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*controlPoints[0][interval_u].getZ() + new_u*new_u*end.getZ();
			
			Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
			
			start=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]);
			end=VertexUtils.meanPoint(controlPoints[1][interval_u], controlPoints[1][interval_u+1]);
			
			float gamma2x=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*controlPoints[1][interval_u].getX() + new_u*new_u*end.getX();
			float gamma2y=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*controlPoints[1][interval_u].getY() + new_u*new_u*end.getY();
			float gamma2z=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*controlPoints[1][interval_u].getZ() + new_u*new_u*end.getZ();
			
			Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
			
			float sigmax=gamma1.getX()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
			float sigmay=gamma1.getY()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
			float sigmaz=gamma1.getZ()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
			
			return new Vertex3D(sigmax, sigmay, sigmaz);
		}
		
		if(interval_v==controlPoints.length-1){
			Vertex3D start=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]);
			Vertex3D end=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u], controlPoints[interval_v-1][interval_u+1]);
			
			float gamma1x=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*controlPoints[interval_v-1][interval_u].getX() + new_u*new_u*end.getX();
			float gamma1y=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*controlPoints[interval_v-1][interval_u].getY() + new_u*new_u*end.getY();
			float gamma1z=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*controlPoints[interval_v-1][interval_u].getZ() + new_u*new_u*end.getZ();
			
			Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
			
			start=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]);
			end=VertexUtils.meanPoint(controlPoints[interval_v][interval_u], controlPoints[interval_v][interval_u+1]);
			
			float gamma2x=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*controlPoints[interval_v][interval_u].getX() + new_u*new_u*end.getX();
			float gamma2y=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*controlPoints[interval_v][interval_u].getY() + new_u*new_u*end.getY();
			float gamma2z=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*controlPoints[interval_v][interval_u].getZ() + new_u*new_u*end.getZ();
			
			Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
			
			float sigmax=VertexUtils.meanPoint(gamma1, gamma2).getX()+new_v*(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
			float sigmay=VertexUtils.meanPoint(gamma1, gamma2).getY()+new_v*(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
			float sigmaz=VertexUtils.meanPoint(gamma1, gamma2).getZ()+new_v*(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
			
			return new Vertex3D(sigmax, sigmay, sigmaz);
		}
		
		
		Vertex3D[] gammas=new Vertex3D[3];
		float gamma_ix;
		float gamma_iy;
		float gamma_iz;
		Vertex3D start;
		Vertex3D end;

		for (int i = 0; i < 3; i++) {
			
			start=VertexUtils.meanPoint(subMatrix[i][0], subMatrix[i][1]);
			end=VertexUtils.meanPoint(subMatrix[i][1], subMatrix[i][2]);
			
			gamma_ix=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*subMatrix[i][1].getX() + new_u*new_u*end.getX();
			gamma_iy=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*subMatrix[i][1].getY() + new_u*new_u*end.getY();
			gamma_iz=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*subMatrix[i][1].getZ() + new_u*new_u*end.getZ();
			
			gammas[i]=new Vertex3D(gamma_ix, gamma_iy, gamma_iz);
			
			//System.out.println(gammas[i]);
		}
		
		start=VertexUtils.meanPoint(gammas[0], gammas[1]);
		end=VertexUtils.meanPoint(gammas[1], gammas[2]);
		
		float sigma_x=(1-new_v)*(1-new_v)*start.getX() + 2*new_v*(1-new_v)*gammas[1].getX() + new_v*new_v*end.getX();
		float sigma_y=(1-new_v)*(1-new_v)*start.getY() + 2*new_v*(1-new_v)*gammas[1].getY() + new_v*new_v*end.getY();
		float sigma_z=(1-new_v)*(1-new_v)*start.getZ() + 2*new_v*(1-new_v)*gammas[1].getZ() + new_v*new_v*end.getZ();
		
		Vertex3D sigma=new Vertex3D(sigma_x, sigma_y, sigma_z);
		
		return sigma;
	}

	public Vertex3D dSigmadu(float u, float v) {
		
		focusOnSubmatrix(u, v);
		
		if(interval_u==0){
			if(interval_v==0){
				float gamma1x=(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getX()-controlPoints[0][0].getX());
				float gamma1y=(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getY()-controlPoints[0][0].getY());
				float gamma1z=(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getZ()-controlPoints[0][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getX()-controlPoints[1][0].getX());
				float gamma2y=(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getY()-controlPoints[1][0].getY());
				float gamma2z=(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getZ()-controlPoints[1][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=gamma1.getX()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
				float sigmay=gamma1.getY()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
				float sigmaz=gamma1.getZ()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else if(interval_v==controlPoints.length-1){
				float gamma1x=(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getX()-controlPoints[controlPoints.length-2][0].getX());
				float gamma1y=(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getY()-controlPoints[controlPoints.length-2][0].getY());
				float gamma1z=(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getZ()-controlPoints[controlPoints.length-2][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getX()-controlPoints[controlPoints.length-1][0].getX());
				float gamma2y=(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getY()-controlPoints[controlPoints.length-1][0].getY());
				float gamma2z=(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getZ()-controlPoints[controlPoints.length-1][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=VertexUtils.meanPoint(gamma1, gamma2).getX()+new_v*(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
				float sigmay=VertexUtils.meanPoint(gamma1, gamma2).getY()+new_v*(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
				float sigmaz=VertexUtils.meanPoint(gamma1, gamma2).getZ()+new_v*(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else {
				float gamma1x=(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getX()-controlPoints[interval_v-1][0].getX());
				float gamma1y=(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getY()-controlPoints[interval_v-1][0].getY());
				float gamma1z=(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getZ()-controlPoints[interval_v-1][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getX()-controlPoints[interval_v][0].getX());
				float gamma2y=(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getY()-controlPoints[interval_v][0].getY());
				float gamma2z=(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getZ()-controlPoints[interval_v][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float gamma3x=(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getX()-controlPoints[interval_v+1][0].getX());
				float gamma3y=(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getY()-controlPoints[interval_v+1][0].getY());
				float gamma3z=(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getZ()-controlPoints[interval_v+1][0].getZ());
				
				Vertex3D gamma3=new Vertex3D(gamma3x, gamma3y, gamma3z);
				
				Vertex3D[] gammas={gamma1,gamma2,gamma3};
				
				Vertex3D start=VertexUtils.meanPoint(gammas[0], gammas[1]);
				Vertex3D end=VertexUtils.meanPoint(gammas[1], gammas[2]);
				
				float sigma_x=(1-new_v)*(1-new_v)*start.getX() + 2*new_v*(1-new_v)*gammas[1].getX() + new_v*new_v*end.getX();
				float sigma_y=(1-new_v)*(1-new_v)*start.getY() + 2*new_v*(1-new_v)*gammas[1].getY() + new_v*new_v*end.getY();
				float sigma_z=(1-new_v)*(1-new_v)*start.getZ() + 2*new_v*(1-new_v)*gammas[1].getZ() + new_v*new_v*end.getZ();
				
				Vertex3D sigma=new Vertex3D(sigma_x, sigma_y, sigma_z);
				return sigma;
			}
		}
		
		if(interval_u==controlPoints[0].length-1){
			if(interval_v==0){
				float gamma1x=(controlPoints[0][interval_u].getX()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getX());
				float gamma1y=(controlPoints[0][interval_u].getY()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getY());
				float gamma1z=(controlPoints[0][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=(controlPoints[1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getX());
				float gamma2y=(controlPoints[1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getY());
				float gamma2z=(controlPoints[1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=gamma1.getX()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
				float sigmay=gamma1.getY()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
				float sigmaz=gamma1.getZ()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else if (interval_v==controlPoints.length-1){
				float gamma1x=(controlPoints[controlPoints.length-2][interval_u].getX()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getX());
				float gamma1y=(controlPoints[controlPoints.length-2][interval_u].getY()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getY());
				float gamma1z=(controlPoints[controlPoints.length-2][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=(controlPoints[controlPoints.length-1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getX());
				float gamma2y=(controlPoints[controlPoints.length-1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getY());
				float gamma2z=(controlPoints[controlPoints.length-1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=VertexUtils.meanPoint(gamma1, gamma2).getX()+new_v*(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
				float sigmay=VertexUtils.meanPoint(gamma1, gamma2).getY()+new_v*(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
				float sigmaz=VertexUtils.meanPoint(gamma1, gamma2).getZ()+new_v*(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else {
				float gamma1x=(controlPoints[interval_v-1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getX());
				float gamma1y=(controlPoints[interval_v-1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getY());
				float gamma1z=(controlPoints[interval_v-1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=(controlPoints[interval_v][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getX());
				float gamma2y=(controlPoints[interval_v][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getY());
				float gamma2z=(controlPoints[interval_v][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float gamma3x=(controlPoints[interval_v+1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getX());
				float gamma3y=(controlPoints[interval_v+1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getY());
				float gamma3z=(controlPoints[interval_v+1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getZ());
				
				Vertex3D gamma3=new Vertex3D(gamma3x, gamma3y, gamma3z);
				
				Vertex3D[] gammas={gamma1,gamma2,gamma3};
				
				Vertex3D start=VertexUtils.meanPoint(gammas[0], gammas[1]);
				Vertex3D end=VertexUtils.meanPoint(gammas[1], gammas[2]);
				
				float sigma_x=(1-new_v)*(1-new_v)*start.getX() + 2*new_v*(1-new_v)*gammas[1].getX() + new_v*new_v*end.getX();
				float sigma_y=(1-new_v)*(1-new_v)*start.getY() + 2*new_v*(1-new_v)*gammas[1].getY() + new_v*new_v*end.getY();
				float sigma_z=(1-new_v)*(1-new_v)*start.getZ() + 2*new_v*(1-new_v)*gammas[1].getZ() + new_v*new_v*end.getZ();
				
				Vertex3D sigma=new Vertex3D(sigma_x, sigma_y, sigma_z);
				
				return sigma;
			}
		}
		
		if(interval_v==0){
			
			Vertex3D start=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]);
			Vertex3D end=VertexUtils.meanPoint(controlPoints[0][interval_u], controlPoints[0][interval_u+1]);
			
			float gamma1x=-2*(1-new_u)*start.getX()+2*(1-new_u)*controlPoints[0][interval_u].getX()-2*new_u*controlPoints[0][interval_u].getX()+2*new_u*end.getX();
			float gamma1y=-2*(1-new_u)*start.getY()+2*(1-new_u)*controlPoints[0][interval_u].getY()-2*new_u*controlPoints[0][interval_u].getY()+2*new_u*end.getY();
			float gamma1z=-2*(1-new_u)*start.getZ()+2*(1-new_u)*controlPoints[0][interval_u].getZ()-2*new_u*controlPoints[0][interval_u].getZ()+2*new_u*end.getZ();
			
			Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
			
			start=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]);
			end=VertexUtils.meanPoint(controlPoints[1][interval_u], controlPoints[1][interval_u+1]);
			
			float gamma2x=-2*(1-new_u)*start.getX()+2*(1-new_u)*controlPoints[1][interval_u].getX()-2*new_u*controlPoints[1][interval_u].getX()+2*new_u*end.getX();
			float gamma2y=-2*(1-new_u)*start.getY()+2*(1-new_u)*controlPoints[1][interval_u].getY()-2*new_u*controlPoints[1][interval_u].getY()+2*new_u*end.getY();
			float gamma2z=-2*(1-new_u)*start.getZ()+2*(1-new_u)*controlPoints[1][interval_u].getZ()-2*new_u*controlPoints[1][interval_u].getZ()+2*new_u*end.getZ();
			
			Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
			
			float sigmax=gamma1.getX()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
			float sigmay=gamma1.getY()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
			float sigmaz=gamma1.getZ()+new_v*(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
			
			return new Vertex3D(sigmax, sigmay, sigmaz);
		}
		
		if(interval_v==controlPoints.length-1){
			Vertex3D start=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]);
			Vertex3D end=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u], controlPoints[interval_v-1][interval_u+1]);
			
			float gamma1x=-2*(1-new_u)*start.getX()+2*(1-new_u)*controlPoints[interval_v-1][interval_u].getX()-2*new_u*controlPoints[interval_v-1][interval_u].getX()+2*new_u*end.getX();
			float gamma1y=-2*(1-new_u)*start.getY()+2*(1-new_u)*controlPoints[interval_v-1][interval_u].getY()-2*new_u*controlPoints[interval_v-1][interval_u].getY()+2*new_u*end.getY();
			float gamma1z=-2*(1-new_u)*start.getZ()+2*(1-new_u)*controlPoints[interval_v-1][interval_u].getZ()-2*new_u*controlPoints[interval_v-1][interval_u].getZ()+2*new_u*end.getZ();
			
			Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
			
			start=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]);
			end=VertexUtils.meanPoint(controlPoints[interval_v][interval_u], controlPoints[interval_v][interval_u+1]);
			
			float gamma2x=-2*(1-new_u)*start.getX()+2*(1-new_u)*controlPoints[interval_v][interval_u].getX()-2*new_u*controlPoints[interval_v][interval_u].getX()+2*new_u*end.getX();
			float gamma2y=-2*(1-new_u)*start.getY()+2*(1-new_u)*controlPoints[interval_v][interval_u].getY()-2*new_u*controlPoints[interval_v][interval_u].getY()+2*new_u*end.getY();
			float gamma2z=-2*(1-new_u)*start.getZ()+2*(1-new_u)*controlPoints[interval_v][interval_u].getZ()-2*new_u*controlPoints[interval_v][interval_u].getZ()+2*new_u*end.getZ();
			
			Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
			
			float sigmax=VertexUtils.meanPoint(gamma1, gamma2).getX()+new_v*(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
			float sigmay=VertexUtils.meanPoint(gamma1, gamma2).getY()+new_v*(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
			float sigmaz=VertexUtils.meanPoint(gamma1, gamma2).getZ()+new_v*(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
			
			return new Vertex3D(sigmax, sigmay, sigmaz);
		}
		

		Vertex3D[] gPrimes=new Vertex3D[3];
		
		float gPrime_ix;
		float gPrime_iy;
		float gPrime_iz;
		
		Vertex3D start;
		Vertex3D end;
		
		
		for (int i = 0; i < 3; i++) {
			
			start = VertexUtils.meanPoint(subMatrix[i][0], subMatrix[i][1]);
			end = VertexUtils.meanPoint(subMatrix[i][1], subMatrix[i][2]);
			
			gPrime_ix = -2*(1-new_u)*start.getX()+2*(1-new_u)*subMatrix[i][1].getX()-2*new_u*subMatrix[i][1].getX()+2*new_u*end.getX();
			gPrime_iy = -2*(1-new_u)*start.getY()+2*(1-new_u)*subMatrix[i][1].getY()-2*new_u*subMatrix[i][1].getY()+2*new_u*end.getY();
			gPrime_iz = -2*(1-new_u)*start.getZ()+2*(1-new_u)*subMatrix[i][1].getZ()-2*new_u*subMatrix[i][1].getZ()+2*new_u*end.getZ();
			
			gPrimes[i]= new Vertex3D(gPrime_ix, gPrime_iy, gPrime_iz);
			
		}
		
		start = VertexUtils.meanPoint(gPrimes[0], gPrimes[1]);
		end = VertexUtils.meanPoint(gPrimes[1], gPrimes[2]);
		
		float dSigmadUX =(1-new_v)*(1-new_v)*start.getX() + 2*new_v*(1-new_v)*gPrimes[1].getX() + new_v*new_v*end.getX();
		float dSigmadUY =(1-new_v)*(1-new_v)*start.getY() + 2*new_v*(1-new_v)*gPrimes[1].getY() + new_v*new_v*end.getY();
		float dSigmadUZ =(1-new_v)*(1-new_v)*start.getZ() + 2*new_v*(1-new_v)*gPrimes[1].getZ() + new_v*new_v*end.getZ();
		
		Vertex3D dSigmadU = new Vertex3D(dSigmadUX, dSigmadUY, dSigmadUZ);
		
		return dSigmadU;
	}

	public Vertex3D dSigmadv(float u, float v) {
		focusOnSubmatrix(u, v);
		
		if(interval_u==0){
			if(interval_v==0){
				float gamma1x=controlPoints[0][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getX()-controlPoints[0][0].getX());
				float gamma1y=controlPoints[0][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getY()-controlPoints[0][0].getY());
				float gamma1z=controlPoints[0][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[0][1], controlPoints[0][0]).getZ()-controlPoints[0][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=controlPoints[1][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getX()-controlPoints[1][0].getX());
				float gamma2y=controlPoints[1][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getY()-controlPoints[1][0].getY());
				float gamma2z=controlPoints[1][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[1][1], controlPoints[1][0]).getZ()-controlPoints[1][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
				float sigmay=(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
				float sigmaz=(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else if(interval_v==controlPoints.length-1){
				float gamma1x=controlPoints[controlPoints.length-2][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getX()-controlPoints[controlPoints.length-2][0].getX());
				float gamma1y=controlPoints[controlPoints.length-2][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getY()-controlPoints[controlPoints.length-2][0].getY());
				float gamma1z=controlPoints[controlPoints.length-2][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-2][1], controlPoints[controlPoints.length-2][0]).getZ()-controlPoints[controlPoints.length-2][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=controlPoints[controlPoints.length-1][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getX()-controlPoints[controlPoints.length-1][0].getX());
				float gamma2y=controlPoints[controlPoints.length-1][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getY()-controlPoints[controlPoints.length-1][0].getY());
				float gamma2z=controlPoints[controlPoints.length-1][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[controlPoints.length-1][1], controlPoints[controlPoints.length-1][0]).getZ()-controlPoints[controlPoints.length-1][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
				float sigmay=(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
				float sigmaz=(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else {
				float gamma1x=controlPoints[interval_v-1][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getX()-controlPoints[interval_v-1][0].getX());
				float gamma1y=controlPoints[interval_v-1][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getY()-controlPoints[interval_v-1][0].getY());
				float gamma1z=controlPoints[interval_v-1][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v-1][1], controlPoints[interval_v-1][0]).getZ()-controlPoints[interval_v-1][0].getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=controlPoints[interval_v][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getX()-controlPoints[interval_v][0].getX());
				float gamma2y=controlPoints[interval_v][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getY()-controlPoints[interval_v][0].getY());
				float gamma2z=controlPoints[interval_v][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v][1], controlPoints[interval_v][0]).getZ()-controlPoints[interval_v][0].getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float gamma3x=controlPoints[interval_v+1][0].getX()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getX()-controlPoints[interval_v+1][0].getX());
				float gamma3y=controlPoints[interval_v+1][0].getY()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getY()-controlPoints[interval_v+1][0].getY());
				float gamma3z=controlPoints[interval_v+1][0].getZ()+new_u*(VertexUtils.meanPoint(controlPoints[interval_v+1][1], controlPoints[interval_v+1][0]).getZ()-controlPoints[interval_v+1][0].getZ());
				
				Vertex3D gamma3=new Vertex3D(gamma3x, gamma3y, gamma3z);
				
				Vertex3D[] gammas={gamma1,gamma2,gamma3};
				
				Vertex3D start=VertexUtils.meanPoint(gammas[0], gammas[1]);
				Vertex3D end=VertexUtils.meanPoint(gammas[1], gammas[2]);
				
				float sigma_x=-2*(1-new_v)*start.getX()+2*(1-new_v)*gammas[1].getX()-2*new_v*gammas[1].getX()+2*new_v*end.getX();
				float sigma_y=-2*(1-new_v)*start.getY()+2*(1-new_v)*gammas[1].getY()-2*new_v*gammas[1].getY()+2*new_v*end.getY();
				float sigma_z=-2*(1-new_v)*start.getZ()+2*(1-new_v)*gammas[1].getZ()-2*new_v*gammas[1].getZ()+2*new_v*end.getZ();
				
				Vertex3D sigma=new Vertex3D(sigma_x, sigma_y, sigma_z);
		
				return sigma;
			}
		}
		
		if(interval_u==controlPoints[0].length-1){
			if(interval_v==0){
				float gamma1x=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getX()+new_u*(controlPoints[0][interval_u].getX()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getX());
				float gamma1y=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getY()+new_u*(controlPoints[0][interval_u].getY()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getY());
				float gamma1z=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getZ()+new_u*(controlPoints[0][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getX()+new_u*(controlPoints[1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getX());
				float gamma2y=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getY()+new_u*(controlPoints[1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getY());
				float gamma2z=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getZ()+new_u*(controlPoints[1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
				float sigmay=(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
				float sigmaz=(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else if (interval_v==controlPoints.length-1){
				float gamma1x=VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getX()+new_u*(controlPoints[controlPoints.length-2][interval_u].getX()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getX());
				float gamma1y=VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getY()+new_u*(controlPoints[controlPoints.length-2][interval_u].getY()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getY());
				float gamma1z=VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getZ()+new_u*(controlPoints[controlPoints.length-2][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[controlPoints.length-2][interval_u-1], controlPoints[controlPoints.length-2][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getX()+new_u*(controlPoints[controlPoints.length-1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getX());
				float gamma2y=VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getY()+new_u*(controlPoints[controlPoints.length-1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getY());
				float gamma2z=VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getZ()+new_u*(controlPoints[controlPoints.length-1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[controlPoints.length-1][interval_u-1], controlPoints[controlPoints.length-1][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float sigmax=(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
				float sigmay=(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
				float sigmaz=(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
				
				return new Vertex3D(sigmax, sigmay, sigmaz);
			} else {
				float gamma1x=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getX()+new_u*(controlPoints[interval_v-1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getX());
				float gamma1y=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getY()+new_u*(controlPoints[interval_v-1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getY());
				float gamma1z=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getZ()+new_u*(controlPoints[interval_v-1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]).getZ());
				
				Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
				
				float gamma2x=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getX()+new_u*(controlPoints[interval_v][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getX());
				float gamma2y=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getY()+new_u*(controlPoints[interval_v][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getY());
				float gamma2z=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getZ()+new_u*(controlPoints[interval_v][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]).getZ());
				
				Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
				
				float gamma3x=VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getX()+new_u*(controlPoints[interval_v+1][interval_u].getX()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getX());
				float gamma3y=VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getY()+new_u*(controlPoints[interval_v+1][interval_u].getY()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getY());
				float gamma3z=VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getZ()+new_u*(controlPoints[interval_v+1][interval_u].getZ()-VertexUtils.meanPoint(controlPoints[interval_v+1][interval_u-1], controlPoints[interval_v+1][interval_u]).getZ());
				
				Vertex3D gamma3=new Vertex3D(gamma3x, gamma3y, gamma3z);
				
				Vertex3D[] gammas={gamma1,gamma2,gamma3};
				
				Vertex3D start=VertexUtils.meanPoint(gammas[0], gammas[1]);
				Vertex3D end=VertexUtils.meanPoint(gammas[1], gammas[2]);
				
				float sigma_x=-2*(1-new_v)*start.getX()+2*(1-new_v)*gammas[1].getX()-2*new_v*gammas[1].getX()+2*new_v*end.getX();
				float sigma_y=-2*(1-new_v)*start.getY()+2*(1-new_v)*gammas[1].getY()-2*new_v*gammas[1].getY()+2*new_v*end.getY();
				float sigma_z=-2*(1-new_v)*start.getZ()+2*(1-new_v)*gammas[1].getZ()-2*new_v*gammas[1].getZ()+2*new_v*end.getZ();
				
				Vertex3D sigma=new Vertex3D(sigma_x, sigma_y, sigma_z);
				
				return sigma;
			}
		}
		
		if(interval_v==0){
			
			Vertex3D start=VertexUtils.meanPoint(controlPoints[0][interval_u-1], controlPoints[0][interval_u]);
			Vertex3D end=VertexUtils.meanPoint(controlPoints[0][interval_u], controlPoints[0][interval_u+1]);
			
			float gamma1x=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*controlPoints[0][interval_u].getX() + new_u*new_u*end.getX();
			float gamma1y=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*controlPoints[0][interval_u].getY() + new_u*new_u*end.getY();
			float gamma1z=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*controlPoints[0][interval_u].getZ() + new_u*new_u*end.getZ();
			
			Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
			
			start=VertexUtils.meanPoint(controlPoints[1][interval_u-1], controlPoints[1][interval_u]);
			end=VertexUtils.meanPoint(controlPoints[1][interval_u], controlPoints[1][interval_u+1]);
			
			float gamma2x=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*controlPoints[1][interval_u].getX() + new_u*new_u*end.getX();
			float gamma2y=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*controlPoints[1][interval_u].getY() + new_u*new_u*end.getY();
			float gamma2z=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*controlPoints[1][interval_u].getZ() + new_u*new_u*end.getZ();
			
			Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
			
			float sigmax=(VertexUtils.meanPoint(gamma1, gamma2).getX()-gamma1.getX());
			float sigmay=(VertexUtils.meanPoint(gamma1, gamma2).getY()-gamma1.getY());
			float sigmaz=(VertexUtils.meanPoint(gamma1, gamma2).getZ()-gamma1.getZ());
			
			return new Vertex3D(sigmax, sigmay, sigmaz);
		}
		
		if(interval_v==controlPoints.length-1){
			Vertex3D start=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u-1], controlPoints[interval_v-1][interval_u]);
			Vertex3D end=VertexUtils.meanPoint(controlPoints[interval_v-1][interval_u], controlPoints[interval_v-1][interval_u+1]);
			
			float gamma1x=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*controlPoints[interval_v-1][interval_u].getX() + new_u*new_u*end.getX();
			float gamma1y=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*controlPoints[interval_v-1][interval_u].getY() + new_u*new_u*end.getY();
			float gamma1z=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*controlPoints[interval_v-1][interval_u].getZ() + new_u*new_u*end.getZ();
			
			Vertex3D gamma1=new Vertex3D(gamma1x, gamma1y, gamma1z);
			
			start=VertexUtils.meanPoint(controlPoints[interval_v][interval_u-1], controlPoints[interval_v][interval_u]);
			end=VertexUtils.meanPoint(controlPoints[interval_v][interval_u], controlPoints[interval_v][interval_u+1]);
			
			float gamma2x=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*controlPoints[interval_v][interval_u].getX() + new_u*new_u*end.getX();
			float gamma2y=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*controlPoints[interval_v][interval_u].getY() + new_u*new_u*end.getY();
			float gamma2z=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*controlPoints[interval_v][interval_u].getZ() + new_u*new_u*end.getZ();
			
			Vertex3D gamma2=new Vertex3D(gamma2x, gamma2y, gamma2z);
			
			float sigmax=(gamma2.getX()-VertexUtils.meanPoint(gamma1, gamma2).getX());
			float sigmay=(gamma2.getY()-VertexUtils.meanPoint(gamma1, gamma2).getY());
			float sigmaz=(gamma2.getZ()-VertexUtils.meanPoint(gamma1, gamma2).getZ());
			
			return new Vertex3D(sigmax, sigmay, sigmaz);
		}
		
		Vertex3D[] gammas=new Vertex3D[3];
		float gamma_ix;
		float gamma_iy;
		float gamma_iz;
		Vertex3D start;
		Vertex3D end;
		
		for (int i = 0; i < 3; i++) {
			
			start=VertexUtils.meanPoint(subMatrix[i][0], subMatrix[i][1]);
			end=VertexUtils.meanPoint(subMatrix[i][1], subMatrix[i][2]);
			
			gamma_ix=(1-new_u)*(1-new_u)*start.getX() + 2*new_u*(1-new_u)*subMatrix[i][1].getX() + new_u*new_u*end.getX();
			gamma_iy=(1-new_u)*(1-new_u)*start.getY() + 2*new_u*(1-new_u)*subMatrix[i][1].getY() + new_u*new_u*end.getY();
			gamma_iz=(1-new_u)*(1-new_u)*start.getZ() + 2*new_u*(1-new_u)*subMatrix[i][1].getZ() + new_u*new_u*end.getZ();
			
			gammas[i]=new Vertex3D(gamma_ix, gamma_iy, gamma_iz);
			
		}
		
		start=VertexUtils.meanPoint(gammas[0], gammas[1]);
		end=VertexUtils.meanPoint(gammas[1], gammas[2]);
		
		float dSigmadVX=-2*(1-new_v)*start.getX()+2*(1-new_v)*gammas[1].getX()-2*new_v*gammas[1].getX()+2*new_v*end.getX();
		float dSigmadVY=-2*(1-new_v)*start.getY()+2*(1-new_v)*gammas[1].getY()-2*new_v*gammas[1].getY()+2*new_v*end.getY();
		float dSigmadVZ=-2*(1-new_v)*start.getZ()+2*(1-new_v)*gammas[1].getZ()-2*new_v*gammas[1].getZ()+2*new_v*end.getZ();
		
		Vertex3D dSigmadV = new Vertex3D(dSigmadVX, dSigmadVY, dSigmadVZ);
	
		return dSigmadV;
	}

	public Vertex3D getNormal(float u, float v) {
		Vertex3D dSigmadU=dSigmadu(u, v);
		Vertex3D dSigmadV=dSigmadv(u, v);
		Vertex3D normalVector = VertexUtils.crossProduct(dSigmadU, dSigmadV);
		
		VertexUtils.normalizeVector(normalVector);
		return normalVector;
	}

	public float getStartUPoint() {
		return 0;
	}

	public float getEndUPoint() {
		return controlPoints[0].length;
	}

	public float getStartVPoint() {
		return 0;
	}

	public float getEndVPoint() {
		return controlPoints.length;
	}
	
	private float normalize(float dim, int length) {
		
		while(dim<0){
			dim+=length;
		}
		while(dim>length){
			dim-=length;
		}
		return dim;
	}
	
	private void focusOnSubmatrix(float u, float v){
		int lengthU=controlPoints[0].length;
		int lengthV=controlPoints.length;
		u=normalize(u, lengthU);
		v=normalize(v, lengthV);
		interval_u=(int) u;
		interval_v=(int) v;
		new_u=u-interval_u;
		new_v=v-interval_v;
		
		if(interval_u==lengthU){
			new_u=1;
			interval_u--;
		}
		if(interval_v==lengthV){
			new_v=1;
			interval_v--;
		}

		//P00=controlPoints[interval_v%lengthV][interval_u%lengthU];
		
		for (int i = 0; i < 3; i++) {
			for (int j = 0; j < 3; j++) {
				if((j!=0 || interval_u!=0)  && (i!=0 || interval_v!=0)){
					subMatrix[i][j]=controlPoints[(interval_v+i-1)%lengthV][(interval_u+j-1)%lengthU];
				}
			}
		}
		
	
	}
}
